/******************************************************************************/
/*                                                                            */
/*  Copyright (c) 2009: ngi-central.org                                       */
/*  All rights reserved.                                                      */
/*                                                                            */
/******************************************************************************/


/// \file
/// This file implements the line_segment geometric primitive.

#ifndef __sgl_line_segment_h__
#define __sgl_line_segment_h__

#include <sgl/global_settings.h>
#include <sgl/conditions.h>
#include <sgl/geometric_primitive.h>
#include <sgl/point.h>
#include <sgl/direction.h>
#include <sgl/vector.h>
#include <boost/static_assert.hpp>
#include <boost/operators.hpp>
#include <boost/concept_check.hpp>

namespace sgl {


/******************************************************************************/
/*                                                                            */
/* line_segment                                                               */
/*                                                                            */
/******************************************************************************/

template <
	/// The dimensionality. 
	//!
	//! This defaults to two dimensions.
	size_t D=2,
	/// The element type. 
	//! 
	//! This type defaults to SGL_FPTYPE (which in turn defaults to double), 
	//! but any other numeric type is also suitable.
	typename T=SGL_FPTYPE>
/// A line_segment in euclidean space.
//!
//! A line_segment originates at some point and is then cast along some 
//! direction for a certain extent. Thus, it can be implemented with a 
//! combination of a point, a direction and an extent.
class line_segment
	/// A line_segment is a geometric primitive.
	: public geometric_primitive<D,T>
	, boost::equality_comparable<sgl::line_segment<D,T>, sgl::line_segment<D,T>
	, boost::additive<sgl::line_segment<D,T>, sgl::vector<D,T>
	, boost::multiplicative<sgl::line_segment<D,T>, T
	> > >
{
	// A line_segment must have two or more dimensions.
	BOOST_STATIC_ASSERT(D >= 2);

public:
	/// Default constructor.
	//!
	//! Constructs a zero line_segment.
	line_segment()
		: origin_()
		, direction_()
		, extent_(0)
	{ }

	/// Construct a line_segment from a point, a direction and an extent.
	line_segment(
		/// The origin of the line_segment.
		sgl::point<D,T> origin,
		/// The direction of the line_segment.
		sgl::direction<D,T> direction,
		/// The extent of the line segment.
		T const& extent)
		: origin_(origin)
		, direction_(direction)
		, extent_(extent)
	{ }
/* TODO
	/// Construct a line_segment from a point, a direction and an extent.
	line_segment(
		/// The origin of the line_segment.
		sgl::point<D,T> from,
		/// The origin of the line_segment.
		sgl::point<D,T> to)
		: origin_(origin)
		, direction_((to-from).get_direction())
		, extent_((to-from).get_length())
	{ }
*/
	/// Copy constructor.
	line_segment(
		/// The right hand side.
		sgl::line_segment<D,T> const& rhs)
		: origin_(rhs.origin_)
		, direction_(rhs.direction_)
		, extent_(rhs.extent_)
	{ }

	/// Copy assignment operator.
	sgl::line_segment<D,T> & operator = (
		/// The right hand side.
		sgl::line_segment<D,T> rhs)
	{
		swap(rhs);
		return *this;
	}

	/// Exchanges the elements of two line_segment objects.
	void swap(
		/// The other line_segment.
		sgl::line_segment<D,T> & rhs)
	{
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		std::swap(origin_, rhs.origin_);
		std::swap(direction_, rhs.direction_);
		std::swap(extent_, rhs.extent_);
	}

	/// Find the type of the geometric primitive.
	//!
	//! /return The type of the primitive.
	geometric_type type() const
	{
		return sgl::geometric_primitive<D,T>::line_segment;
	} 

	/// Access the origin of the line_segment.
	//!
	//! /return Returns a reference to the origin of the line_segment.
	sgl::point<D,T> & origin()
	{
		return origin_;
	}

	/// Access the origin of the line_segment.
	//!
	//! /return Returns a const reference to the origin of the line_segment.
	sgl::point<D,T> const & origin() const
	{
		return origin_;
	}

	/// Access the direction of the line_segment.
	//!
	//! /return Returns a reference to the direction of the line_segment.
	sgl::direction<D,T> & direction()
	{
		return direction_;
	}

	/// Access the direction of the line_segment.
	//!
	//! /return Returns a const reference to the direction of the line_segment.
	sgl::direction<D,T> const & direction() const
	{
		return direction_;
	}

	/// Access the extent of the line_segment.
	//!
	//! /return Returns a reference to the extent of the line_segment.
	T & extent()
	{
		return extent_;
	}

	/// Access the extent of the line_segment.
	//!
	//! /return Returns a const reference to the extent of the line_segment.
	T const & extent() const
	{
		return extent_;
	}

	/// Unary negation.
	//!
	//! A line_segment is negated by negating its direction while leaving its 
	//! origin and extent untouched.
	//!
	//! \return A negated line_segment object.
    sgl::line_segment<D,T> operator - () const
    {
        return sgl::line_segment<D,T>(origin_, -direction_, extent_);
    }

	/// Equality operator.
	//!
	//! A line segment is equal to another if the origin, the direction and the
	//! extent are all equal.
	//!
	//! Through the use of boost::equality as a base class operator != is 
	//! defined as well and implemented in terms of this operator ==.
	//!
	//! \return true if equal, false otherwise.
    bool operator == (
		/// Right hand side.
		sgl::line_segment<D,T> const& rhs) const
    {
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		return 
			origin_ == rhs.origin_ &&
			direction_ == rhs.direction_ &&
			extent_ == rhs.extent_;
    }

	/// Add a vector to a line_segment.
	//!
	//! Adding a vector to a line segment moves it in direction of the vector.
	//! This is implemented by adding the vector to the origin of the 
	//! line_segment.
	//!
	//! Through the use of boost::additive as a base class operator + is 
	//! defined as well and implemented in terms of this operator +=.
	//!
	//! \return A reference to this object.
    sgl::line_segment<D,T> & operator += (
		/// Right hand side.
		sgl::vector<D,T> const& rhs)
    {
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		origin_ += rhs;

        return *this;
    }

	/// Subtract a vector from a line_segment.
	//!
	//! Subtracting a vector from a line segment moves it in the direction 
	//! opposite to the vector. This is implemented by subtracting the vector 
	//! from the origin of the line_segment.
	//!
	//! Through the use of boost::additive as a base class operator - is 
	//! defined as well and implemented in terms of this operator -=.
	//!
	//! \return A reference to this object.
    sgl::line_segment<D,T> & operator -= (
		/// Right hand side.
		sgl::vector<D,T> const& rhs)
    {
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		origin_ -= rhs;

		return *this;
    }

	/// Scale a line_segment by multiplication.
	//!
	//! A line_segment is scaled by multiplying its extent with the factor. The
	//! scaling operation affects the length of the line_segment.
	//!
	//! Through the use of boost::multiplicative as a base class operator * is 
	//! defined as well and implemented in terms of this operator *=.
	//!
	//! \return A reference to this object.
	sgl::line_segment<D,T> & operator *= (
		/// The scaling factor.
		T const& factor)
	{
		extent_ *= factor;

		return *this;
	}

	/// Scale a line_segment by division.
	//!
	//! A line_segment is scaled by dividing its extent through the divisor. The
	//! scaling operation affects the length of the line_segment.
	//!
	//! Through the use of boost::multiplicative as a base class operator / is 
	//! defined as well and implemented in terms of this operator /=.
	//!
	//! \return A reference to this object.
	sgl::line_segment<D,T> & operator /= (
		/// The scaling factor.
		T const& divisor)
	{
		extent_ /= divisor;

		return *this;
	}

private:
	// The starting point of the line_segment.
	sgl::point<D,T> origin_;

	/// The direction of the line_segment.
	sgl::direction<D,T> direction_;

	/// The extent of the line_segment;
	T extent_;
};


/// A two-dimensional line_segment with the default component type.
typedef sgl::line_segment<2> line_segment2;

/// A two-dimensional line_segment with float component type.
typedef sgl::line_segment<2, float> line_segment2f;

/// A two-dimensional line_segment with double component type.
typedef sgl::line_segment<2, double> line_segment2d;


/// A three-dimensional line_segment with the default component type.
typedef sgl::line_segment<3> line_segment3;

/// A three-dimensional line_segment with float component type.
typedef sgl::line_segment<3, float> line_segment3f;

/// A three-dimensional line_segment with double component type.
typedef sgl::line_segment<3, double> line_segment3d;


} // namespace sgl

#endif // __sgl_line_segment_h__
